/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2022. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * Description:
 * Author: huawei
 * Create: 2019-10-15
 */

#include <linux/slab.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/pid.h>
#include <linux/kref.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
#include <linux/sched/task.h>
#endif

#include "svm_msg_client.h"
#include "devmm_channel.h"
#include "devdrv_manager_comm.h"
#include "devmm_common.h"
#include "svm_heap_mng.h"
#include "svm_dev_res_mng.h"
#include "svm_shmem_interprocess.h"

#define SVM_IPC_NODE_BUCKET(vptr) (((vptr) >> DEVMM_IPC_NODE_ADDR_SHIFT) & (DEVMM_MAX_IPC_NODE_LIST_NUM - 1))
#define SVM_IPC_OWNER_ID 0

#define DEVMM_IPC_MEM_NAME_SIZE (65)

#define DEVMM_RANDOM_LENGTH 18
#define DEVMM_IPC_NAME_SIZE 65

#ifndef DEVMM_UT
#define DEVMM_MAX_IPC_MEM_NUM 480000
#else
#define DEVMM_MAX_IPC_MEM_NUM 8 /* for ut to cover edges */
#endif
#define DEVMM_IPC_NODE_ADDR_SHIFT  21 /* 2M mask */

struct devmm_proc_ipc_node {
    void *ipc_mem_node;
    u8 idx;
    struct list_head list;
};

struct white_list_proc {
    pid_t pid;
    u64 set_time;
};

struct ipc_mem_node {
    char name[DEVMM_IPC_MEM_NAME_SIZE];
    struct hlist_node link;
    struct rw_semaphore ipc_sema;  /* pa unmap or pa remap */
    struct mutex node_mutex;       /* node open and close assigning */
    struct white_list_proc white_list[DEVMM_SECONDARY_PROCESS_NUM];
    pid_t pid[DEVMM_SECONDARY_PROCESS_NUM];
    void *svm_proc[DEVMM_SECONDARY_PROCESS_NUM];
    u64 vptr[DEVMM_SECONDARY_PROCESS_NUM];
    u64 len;
    u32 page_size;
    int is_huge;
    u32 logical_devid;
    u32 devid;
    u32 vfid;
    u32 valid;
    u32 sequence;  /* use to printf and get node info form record */
    bool is_reserve_addr;
    /*
    * ref create set to 1, devmm_ipc_get_node + 1, must call devmm_ipc_put_node to -1
    * ipc open + 1, must call close -1, create node call destory -1
    * when ref = zero del node
    */
    atomic_t ref;
};

STATIC u64 ipc_name_ref = 0;

static int devmm_ipc_mem_destroy(struct devmm_svm_process *svm_proc, const char *name);
static int devmm_ipc_mem_close(struct devmm_svm_process *svm_proc, u64 vptr);

STATIC int devmm_creat_ipc_name(char *ipc_name, u32 len, u32 devid, u32 vfid)
{
    char random[DEVMM_RANDOM_LENGTH] = {0};
    u64 tmp_ref;
    int offset;
    pid_t tgid;
    int i;

    tgid = current->tgid;
    tmp_ref = (u64)atomic64_inc_return((atomic64_t *)&ipc_name_ref);
    offset = snprintf_s(ipc_name, DEVMM_IPC_NAME_SIZE, DEVMM_IPC_NAME_SIZE - 1,
        "%08x%016x%02x%02x", tgid, tmp_ref, devid, vfid);
    if (offset < 0) {
        devmm_drv_err("Snprintf failed. (offset=%d)\n", offset);
        return -EINVAL;
    }
    for (i = 0; i < DEVMM_RANDOM_LENGTH; i++) {
        int ret = snprintf_s(ipc_name + offset, DEVMM_IPC_NAME_SIZE - offset, DEVMM_IPC_NAME_SIZE - 1 - offset,
            "%02x", (u8)random[i]);
        if (ret < 0) {
            devmm_drv_err("Snprintf failed. (ret=%d)\n", ret);
            return -EINVAL;
        }
        offset += ret;
    }
    ipc_name[DEVMM_IPC_NAME_SIZE - 1] = '\0';
    return 0;
}

STATIC int devmm_ipc_get_id_inst_by_name(const char *name, struct svm_id_inst *id_inst)
{
    u32 devid, vfid;
    u64 tmp_ref;
    pid_t tgid;
    int ret;

    ret = sscanf_s(name, "%08x%016x%02x%02x", &tgid, &tmp_ref, &devid, &vfid);
    if (ret != 4) { /* 4 for para nums */
        devmm_drv_err("Sscanf failed. (ret=%d)\n", ret);
        return -EINVAL;
    }

    if ((devid >= DEVMM_MAX_DEVICE_NUM) || (vfid >= DEVMM_MAX_VF_NUM)) {
        devmm_drv_err("Para invalid. (devid=%u; vfid=%u)\n", devid, vfid);
        return -EINVAL;
    }

    id_inst->devid = devid;
    id_inst->vfid = vfid;
    return 0;
}

STATIC u32 devmm_mc_get_str_elfhash(const u8 *ipc_name)
{
    u32 devid, vfid;
    u64 tmp_ref;
    pid_t tgid;
    int ret;

    ret = sscanf_s(ipc_name, "%08x%016x%02x%02x", &tgid, &tmp_ref, &devid, &vfid);
    if (ret != 4) { /* 4 for para nums */
#ifndef EMU_ST
        devmm_drv_warn("Sscanf warn. (ret=%d)\n", ret);
        return 0;
#endif
    }

    return (tmp_ref % DEVMM_HASH_LIST_NUM);
}

static int _devmm_ipc_node_create(struct devmm_ipc_mem_node_info *info,
    const char *ipc_name, struct ipc_mem_node **ipc_node)
{
    u32 tag = devmm_mc_get_str_elfhash((u8 *)ipc_name);
    struct ipc_mem_node *mem_node = NULL;
    int i;

    mem_node = kzalloc(sizeof(*mem_node), GFP_ATOMIC | __GFP_ACCOUNT);
    if (mem_node == NULL) {
        devmm_drv_err("Kzalloc failed.\n");
        return -ENOMEM;
    }

    for (i = 0; i < DEVMM_IPC_MEM_NAME_SIZE; i++) {
        mem_node->name[i] = ipc_name[i];
    }

    write_lock(&info->rwlock);
    hash_add(info->node_htable, &mem_node->link, tag);
    write_unlock(&info->rwlock);
    *ipc_node = mem_node;
    return 0;
}

static void _devmm_ipc_put_node(struct devmm_ipc_mem_node_info *info, struct ipc_mem_node *ipc_node)
{
    write_lock(&info->rwlock);
    if (atomic_dec_return(&ipc_node->ref) == 0) {
        hash_del(&ipc_node->link);
        devmm_drv_debug("Delete success. (idx=%u)\n", ipc_node->sequence);
        devmm_drv_err_if(memset_s(ipc_node->name, sizeof(ipc_node->name), 0, sizeof(ipc_node->name)) != 0,
            "Memset error.\n");
        kfree(ipc_node);
    }
    write_unlock(&info->rwlock);
}

static int _devmm_ipc_get_node(struct devmm_ipc_mem_node_info *info,
    const char *ipc_name, struct ipc_mem_node **ipc_node)
{
    u32 tag = devmm_mc_get_str_elfhash((u8 *)ipc_name);
    struct ipc_mem_node *mem_node = NULL;

    read_lock(&info->rwlock);
    hash_for_each_possible(info->node_htable, mem_node, link, tag)
    {
        if (strcmp(mem_node->name, ipc_name) == 0) {
            atomic_inc(&mem_node->ref);
            read_unlock(&info->rwlock);
            *ipc_node = mem_node;
            devmm_drv_debug("Get success. (idx=%d; tag=%u)\n", mem_node->sequence, tag);
            return 0;
        }
    }
    read_unlock(&info->rwlock);

    return -EINVAL;
}

STATIC int devmm_ipc_node_create(const char *ipc_name, u32 devid, u32 vfid, struct ipc_mem_node **ipc_node)
{
    struct devmm_dev_res_mng *dev_res_mng = NULL;
    struct svm_id_inst id_inst;
    int ret;

    svm_id_inst_pack(&id_inst, devid, vfid);
    dev_res_mng = devmm_dev_res_mng_get(&id_inst);
    if (dev_res_mng == NULL) {
        return -EINVAL;
    }

    ret = _devmm_ipc_node_create(&dev_res_mng->ipc_mem_node_info, ipc_name, ipc_node);
    devmm_dev_res_mng_put(dev_res_mng);
    return 0;
}

STATIC void devmm_ipc_put_node(struct ipc_mem_node *ipc_node)
{
    struct devmm_dev_res_mng *dev_res_mng = NULL;
    struct svm_id_inst id_inst;

    svm_id_inst_pack(&id_inst, ipc_node->devid, ipc_node->vfid);
    dev_res_mng = devmm_dev_res_mng_get(&id_inst);
    if (dev_res_mng == NULL) {
        return;
    }

    _devmm_ipc_put_node(&dev_res_mng->ipc_mem_node_info, ipc_node);
    devmm_dev_res_mng_put(dev_res_mng);
}

STATIC int devmm_ipc_get_node(const char *ipc_name, struct ipc_mem_node **ipc_node)
{
    struct devmm_dev_res_mng *dev_res_mng = NULL;
    struct svm_id_inst id_inst;
    int ret;

    ret = devmm_ipc_get_id_inst_by_name(ipc_name, &id_inst);
    if (ret != 0) {
        return ret;
    }

    dev_res_mng = devmm_dev_res_mng_get(&id_inst);
    if (dev_res_mng == NULL) {
        return -ENODEV;
    }

    ret = _devmm_ipc_get_node(&dev_res_mng->ipc_mem_node_info, ipc_name, ipc_node);
    devmm_dev_res_mng_put(dev_res_mng);
    return ret;
}

STATIC int devmm_ipc_mem_check_pid(struct ipc_mem_node *node)
{
    u64 start_time = devmm_get_tgid_start_time();
    pid_t pid = devmm_get_current_pid();
    int i;

    for (i = 0; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
        if (pid == node->pid[i]) {
            devmm_drv_err("Already opened. (thread=%d; vptr=0x%llx; index=%d)\n",
                node->pid[i], node->vptr[i], i);
            return -EINVAL;
        }
    }
    for (i = 0; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
        if (pid == node->white_list[i].pid && start_time <= node->white_list[i].set_time) {
            return 0;
        }
    }
    devmm_drv_err("This node not allowed open. (thread=%d; thread_start_time=%llu)\n", pid, start_time);
    return -EINVAL;
}

static struct devmm_proc_ipc_node *devmm_search_proc_ipc_node(struct devmm_svm_process *svm_proc, u64 vptr, u64 len)
{
    struct devmm_proc_ipc_node *ipc_node = NULL;
    struct ipc_mem_node *node = NULL;
    struct list_head *pos = NULL;
    struct list_head *n = NULL;
    int head_idx;
    u8 idx;

    head_idx = SVM_IPC_NODE_BUCKET(vptr);
    list_for_each_safe(pos, n, &svm_proc->ipc_node.head[head_idx]) {
        ipc_node = (struct devmm_proc_ipc_node *)list_entry(pos, struct devmm_proc_ipc_node, list);
        node = (struct ipc_mem_node *)ipc_node->ipc_mem_node;

        idx = ipc_node->idx;

        if ((node->pid[idx] == svm_proc->process_id.hostpid) && (node->vptr[idx] == vptr) &&
            ((node->len == len) || (len == 0))) {
            return ipc_node;
        }
    }

    return NULL;
}

static struct ipc_mem_node *devmm_search_ipc_node(struct devmm_svm_process *svm_proc, u64 vptr, u64 len)
{
    struct devmm_proc_ipc_node *ipc_node = devmm_search_proc_ipc_node(svm_proc, vptr, len);

    if (ipc_node == NULL) {
        return NULL;
    }

    return (struct ipc_mem_node *)ipc_node->ipc_mem_node;
}

static bool devmm_is_ipc_mem_node_create_by_proc(struct devmm_svm_process *svm_proc, struct ipc_mem_node *node)
{
    return (node->svm_proc[SVM_IPC_OWNER_ID] == svm_proc);
}

STATIC int devmm_ipc_mem_get_open_index(struct ipc_mem_node *node, pid_t pid, unsigned long vptr)
{
    int i;

    for (i = 0; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
        /* each process can open / create same node once */
        if ((node->pid[i] == pid) && ((vptr == 0) || (node->vptr[i] == vptr))) {
            return i;
        }
    }

    return DEVMM_SVM_INVALID_INDEX;
}

STATIC int devmm_ipc_mem_get_idle_index(struct ipc_mem_node *node, pid_t pid, unsigned long vptr)
{
    int i;

    /* 0 is refed for create process */
    for (i = 1; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
        if (node->pid[i] == DEVMM_SVM_INVALID_PID) {
            node->pid[i] = pid;
            node->vptr[i] = vptr;
            return i;
        }
    }
    return DEVMM_SVM_INVALID_INDEX;
}

static void devmm_ipc_mem_node_clear_pid(struct ipc_mem_node *node, u32 index)
{
    node->pid[index] = DEVMM_SVM_INVALID_PID;
    node->vptr[index] = 0;
}

static void devmm_ipc_mem_remove_proc_from_node(struct ipc_mem_node *node, u32 index)
{
    devmm_ipc_mem_node_clear_pid(node, index);
    node->svm_proc[index] = NULL;
}

static void devmm_ipc_mem_add_proc_to_node(struct devmm_svm_process *svm_proc, struct ipc_mem_node *node, u32 index)
{
    node->svm_proc[index] = svm_proc;
}


static void devmm_ipc_mem_set_ipc_create_flag(struct devmm_svm_process *svm_proc, u64 va)
{
    u32 *bitmap = devmm_get_page_bitmap(svm_proc, va);
    if (bitmap != NULL) {
        devmm_page_bitmap_set_flag(bitmap, DEVMM_PAGE_IPC_MEM_CREATE_MASK);
    }
}

static void devmm_ipc_mem_set_fst_page_ipc_create_flag(struct devmm_svm_process *svm_proc, u64 va)
{
    u32 *bitmap = devmm_get_alloced_va_fst_page_bitmap(svm_proc, va);
    if (bitmap != NULL) {
        devmm_page_bitmap_set_flag(bitmap, DEVMM_PAGE_IPC_MEM_CREATE_MASK);
    }
}

static void devmm_ipc_mem_set_ipc_open_lock_flag(struct devmm_svm_process *svm_proc, u64 va, u64 page_num)
{
    u32 *bitmap = devmm_get_page_bitmap(svm_proc, va);
    u64 i;

    for (i = 0; i < page_num; i++) {
        devmm_page_bitmap_set_flag(bitmap + i, DEVMM_PAGE_IPC_MEM_OPEN_MASK);
        devmm_page_bitmap_set_flag(bitmap + i, DEVMM_PAGE_LOCKED_DEVICE_MASK);
    }
}

static void devmm_ipc_mem_clear_ipc_open_lock_flag(struct devmm_svm_process *svm_proc, u64 va, u64 page_num)
{
    u32 *bitmap = devmm_get_page_bitmap(svm_proc, va);
    u64 i;

    if (bitmap == NULL) {
        return;
    }
    for (i = 0; i < page_num; i++) {
        devmm_page_bitmap_clear_flag(bitmap + i, DEVMM_PAGE_IPC_MEM_OPEN_MASK);
        devmm_page_bitmap_clear_flag(bitmap + i, DEVMM_PAGE_LOCKED_DEVICE_MASK);
    }
}

static int devmm_ipc_mem_add_node_to_proc(struct devmm_svm_process *svm_proc, struct ipc_mem_node *node, u8 idx)
{
    struct devmm_proc_ipc_node *proc_mem_node = NULL;
    int head_idx;

    if (svm_proc->ipc_node.node_cnt >= DEVMM_MAX_IPC_MEM_NUM) {
        devmm_drv_err("No idle ipc memory node. (pid=%d; devid=%u; shm_node_cnt=%u)\n",
            svm_proc->process_id.hostpid, node->devid, svm_proc->ipc_node.node_cnt);
        return -EINVAL;
    }
    proc_mem_node = kzalloc(sizeof(struct devmm_proc_ipc_node), GFP_KERNEL | __GFP_ACCOUNT);
    if (proc_mem_node == NULL) {
        devmm_drv_err("Kzalloc fail. (pid=%d; devid=%u; len=%llu; is_huge=%d)\n",
            svm_proc->process_id.hostpid, node->devid, node->len, node->is_huge);
        return -ENOMEM;
    }
    proc_mem_node->ipc_mem_node = node;
    proc_mem_node->idx = idx;
    svm_proc->ipc_node.node_cnt++;
    head_idx = SVM_IPC_NODE_BUCKET(node->vptr[idx]);
    list_add(&proc_mem_node->list, &svm_proc->ipc_node.head[head_idx]);

    return 0;
}

static void devmm_ipc_mem_remove_node_from_proc(struct devmm_svm_process *svm_proc,
    struct devmm_proc_ipc_node *ipc_node)
{
    devmm_drv_debug("Decrease ipc memory node. (pid=%d; shm_node_cnt=%u)\n",
        svm_proc->process_id.hostpid, svm_proc->ipc_node.node_cnt);
    svm_proc->ipc_node.node_cnt--;
    list_del(&ipc_node->list);
    kfree(ipc_node);
}

static void devmm_ipc_mem_node_init(struct ipc_mem_node *node, struct devmm_svm_process *svm_process,
    u64 vptr, size_t len, struct devmm_memory_attributes *attr)
{
    int i;

    init_rwsem(&node->ipc_sema);
    mutex_init(&node->node_mutex);
    atomic_set(&node->ref, 1);
    node->len = len;
    node->page_size = attr->page_size;
    node->is_huge = attr->is_svm_huge;
    node->logical_devid = attr->logical_devid;
    node->devid = attr->devid;
    node->vfid = attr->vfid;
    node->pid[SVM_IPC_OWNER_ID] = devmm_get_current_pid();
    node->vptr[SVM_IPC_OWNER_ID] = vptr;
    node->white_list[0].pid = devmm_get_current_pid();
    node->white_list[0].set_time = 0;
    node->is_reserve_addr = attr->is_reserve_addr;
    for (i = 1; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
        node->pid[i] = DEVMM_SVM_INVALID_PID;
        node->white_list[i].pid = DEVMM_SVM_INVALID_PID;
        node->white_list[i].set_time = 0;
        node->svm_proc[i] = NULL;
        node->vptr[i] = 0;
    }
    node->sequence = (u32)atomic_inc_return(&devmm_svm->ipcnode_sq);
}

STATIC void devmm_set_ipc_mem_node_valid_status(struct ipc_mem_node *node, u32 valid)
{
    node->valid = valid;
}

STATIC bool devmm_is_ipc_mem_node_valid(struct ipc_mem_node *node)
{
    return (node->valid == 1);
}

static void devmm_free_ipc_open_pages(struct devmm_svm_process *svm_proc, u64 vptr, u64 page_num, u32 page_size)
{
    struct devmm_chan_free_pages free_pgs;
    u32 *bitmap = NULL;
    u64 cnt, i;
    int ret;

    bitmap = devmm_get_page_bitmap(svm_proc, vptr);
    if (bitmap == NULL) {
        devmm_drv_err("Va isn't alloced. (va=0x%llx)\n", vptr);
        return;
    }
    /* not clear bitmap when device fault know this memory alloc by ipc open is destoryed. */
    cnt = page_num / DEVMM_FREE_SECTION_NUM;
    if ((page_num % DEVMM_FREE_SECTION_NUM) > 0) {
        cnt++;
    }

    free_pgs.head.msg_id = DEVMM_CHAN_FREE_PAGES_H2D_ID;
    free_pgs.head.process_id.devid = (u16)devmm_page_bitmap_get_phy_devid(svm_proc, bitmap);
    free_pgs.head.process_id.hostpid = svm_proc->process_id.hostpid;
    free_pgs.head.process_id.vfid = (u16)devmm_page_bitmap_get_vfid(svm_proc, bitmap);
    free_pgs.head.dev_id = free_pgs.head.process_id.devid;
    free_pgs.real_size = DEVMM_FREE_SECTION_NUM * page_size;

    for (i = 0; i < cnt; i++) {
        free_pgs.va = vptr + i * DEVMM_FREE_SECTION_NUM * page_size;

        if (i == cnt - 1) {
            free_pgs.real_size = (page_num % DEVMM_FREE_SECTION_NUM) * page_size;
        }

        ret = devmm_chan_msg_send(&free_pgs, sizeof(struct devmm_chan_free_pages), 0);
        if (ret != 0) {
            devmm_drv_warn("Free page error. (dev_id=%d; ret=%d; va=0x%llx; realsize=%llu)\n",
                           free_pgs.head.dev_id, ret, free_pgs.va, free_pgs.real_size);
            return;
        }
    }
}

static void devmm_ipc_mem_free_open_proc_pages(struct ipc_mem_node *node)
{
    struct devmm_svm_process *svm_proc = NULL;
    u32 *bitmap = NULL;
    u32 i;

    for (i = 0; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
        if ((i == SVM_IPC_OWNER_ID) || (node->svm_proc[i] == NULL)) {
            continue;
        }

        svm_proc = node->svm_proc[i];
        if (devmm_svm_other_proc_occupy_num_add(svm_proc) != 0) {
            devmm_drv_debug("Process is exiting. (hostpid=%d; va=0x%llx)\n",
                svm_proc->process_id.hostpid, node->vptr[i]);
            continue;
        }

        down_write(&svm_proc->bitmap_sem);
        bitmap = devmm_get_page_bitmap(svm_proc, node->vptr[i]);
        if (bitmap == NULL) {
            devmm_drv_warn("Get bitmap failed. (hostpid=%d; va=0x%llx)\n",
                svm_proc->process_id.hostpid, node->vptr[i]);
            goto next;
        }

        if (!devmm_page_bitmap_is_dev_mapped(bitmap)) {
            devmm_drv_debug("Device not map. (hostpid=%d; va=0x%llx)\n",
                svm_proc->process_id.hostpid, node->vptr[i]);
            goto next;
        }

        devmm_drv_debug("Open free success. (node=\"%s\"; hostpid=%d; va=0x%llx)\n",
            node->name, svm_proc->process_id.hostpid, node->vptr[i]);

        devmm_free_ipc_open_pages(svm_proc, node->vptr[i],
            devmm_get_pagecount_by_size(node->vptr[i], node->len, node->page_size), node->page_size);

next:
        up_write(&svm_proc->bitmap_sem);
        devmm_svm_other_proc_occupy_num_sub(svm_proc);
    }
}

void devmm_try_free_ipc_mem(struct devmm_svm_process *svm_proc, u64 vptr, u64 page_num, u32 page_size)
{
    u32 *bitmap = devmm_get_page_bitmap(svm_proc, vptr);
    struct devmm_proc_ipc_node *ipc_node = NULL;
    struct ipc_mem_node *node = NULL;
    struct list_head *pos = NULL;
    struct list_head *n = NULL;
    u64 len = page_num * page_size;
    u64 page_id;
    int head_idx;
    u8 idx;

    /* just for zeroDivision warning clear */
    if (page_size == 0) {
        return;
    }

    mutex_lock(&svm_proc->ipc_node.node_mutex);
    for (head_idx = 0; head_idx < DEVMM_MAX_IPC_NODE_LIST_NUM; head_idx++) {
        list_for_each_safe(pos, n, &svm_proc->ipc_node.head[head_idx]) {
            ipc_node = (struct devmm_proc_ipc_node *)list_entry(pos, struct devmm_proc_ipc_node, list);
            idx = ipc_node->idx;

            /* not create, it is ipc open */
            if (idx != SVM_IPC_OWNER_ID) {
                continue;
            }

            node = (struct ipc_mem_node *)ipc_node->ipc_mem_node;
            if ((node->vptr[idx] < vptr) || (node->vptr[idx] >= (vptr + len))) {
                continue;
            }

            if (!devmm_is_ipc_mem_node_valid(node)) {
                continue;
            }

            page_id = (node->vptr[idx] - vptr) / page_size;
            if (!devmm_page_bitmap_is_ipc_create_mem((bitmap + page_id))) {
                devmm_drv_warn("Vaddress is not created. (va=0x%llx; page_id=0x%lld)\n", vptr, page_id);
                continue;
            }

            atomic_inc(&node->ref);

            devmm_drv_debug("Free vaddress details. (va=0x%llx; node_va=0x%llx; node=\"%s\"; ref=%d)\n",
                vptr, node->vptr[idx], node->name, atomic_read(&node->ref));

            mutex_lock(&node->node_mutex);
            devmm_set_ipc_mem_node_valid_status(node, 0);
            devmm_ipc_mem_free_open_proc_pages(node);
            mutex_unlock(&node->node_mutex);
            devmm_ipc_put_node(node);
            cond_resched();
        }
    }
    mutex_unlock(&svm_proc->ipc_node.node_mutex);
}

void devmm_destroy_ipc_mem_node_by_proc(struct devmm_svm_process *svm_proc)
{
    struct devmm_proc_ipc_node *ipc_node = NULL;
    struct ipc_mem_node *node = NULL;
    struct list_head *pos = NULL;
    struct list_head *n = NULL;
    int head_idx;
    u8 idx;

    mutex_lock(&svm_proc->ipc_node.node_mutex);
    for (head_idx = 0; head_idx < DEVMM_MAX_IPC_NODE_LIST_NUM; head_idx++) {
        list_for_each_safe(pos, n, &svm_proc->ipc_node.head[head_idx]) {
            ipc_node = (struct devmm_proc_ipc_node *)list_entry(pos, struct devmm_proc_ipc_node, list);
            idx = ipc_node->idx;
            node = (struct ipc_mem_node *)ipc_node->ipc_mem_node;

            devmm_drv_debug("Destroy details. (node=\"%s\"; va=%llx)\n", node->name, node->vptr[idx]);

            if (idx == SVM_IPC_OWNER_ID) {
                (void)devmm_ipc_mem_destroy(svm_proc, (const char *)node->name);
                cond_resched();
            } else {
                (void)devmm_ipc_mem_close(svm_proc, node->vptr[idx]);
            }
        }
    }
    mutex_unlock(&svm_proc->ipc_node.node_mutex);
}

static struct devmm_svm_process *devmm_ipc_query_owner_info_by_open_addr(struct devmm_svm_process *svm_proc,
    u64 vptr, u64 *va, struct devmm_svm_process_id *id)
{
    struct devmm_svm_process *owner_proc = NULL;
    struct devmm_proc_ipc_node *ipc_node = NULL;
    struct ipc_mem_node *node = NULL;

    mutex_lock(&svm_proc->ipc_node.node_mutex);
    ipc_node = devmm_search_proc_ipc_node(svm_proc, vptr, 0);
    if (ipc_node == NULL) {
        devmm_drv_err("Search ipc memory node failed. (vptr=0x%llx)\n", vptr);
        goto out;
    }

    if (ipc_node->idx == SVM_IPC_OWNER_ID) {
        devmm_drv_err("Vptr in created self. (vptr=0x%llx)\n", vptr);
        goto out;
    }

    node = (struct ipc_mem_node *)ipc_node->ipc_mem_node;

    if (devmm_is_ipc_mem_node_create_by_proc(svm_proc, node)) {
        devmm_drv_err("Vptr in created self process. (vptr=%llx; node=\"%s\")\n", vptr, node->name);
        goto out;
    }

    *va = node->vptr[SVM_IPC_OWNER_ID];
    id->devid = (u16)node->devid;
    id->vfid = (u16)node->vfid;
    id->hostpid = (int)node->pid[SVM_IPC_OWNER_ID];

    owner_proc = node->svm_proc[SVM_IPC_OWNER_ID];

out:
    mutex_unlock(&svm_proc->ipc_node.node_mutex);
    return owner_proc;
}

struct devmm_svm_process *devmm_ipc_query_owner_info(struct devmm_svm_process *svm_proc,
    u64 va, u32 page_size, u64 *owner_va, struct devmm_svm_process_id *id)
{
    struct devmm_svm_process *owner_proc = NULL;
    u64 alloced_va;
    int ret;

    ret = devmm_get_alloced_va(svm_proc, va, &alloced_va);
    if (ret != 0) {
        devmm_drv_err("Get alloced va failed. (va=%llx)\n", va);
        return NULL;
    }

    owner_proc = devmm_ipc_query_owner_info_by_open_addr(svm_proc, alloced_va, owner_va, id);
    if (owner_proc == NULL) {
        devmm_drv_err("Query ipc onwer info failed. (va=%llx)\n", alloced_va);
        return NULL;
    }

    *owner_va += (va - alloced_va);

    return owner_proc;
}

int devmm_ipc_get_owner_proc_attr(struct devmm_svm_process *svm_proc, struct devmm_memory_attributes *attr,
    struct devmm_svm_process **owner_proc, struct devmm_memory_attributes *owner_attr)
{
    struct devmm_svm_process_id proc_id;
    u64 owner_va;
    int ret;

    *owner_proc = devmm_ipc_query_owner_info(svm_proc, attr->va, attr->page_size, &owner_va, &proc_id);
    if (*owner_proc == NULL) {
        return -EINVAL;
    }

    ret = devmm_svm_other_proc_occupy_get_lock(*owner_proc);
    if (ret != 0) {
        devmm_drv_debug("Process is exiting. (hostpid=%d)\n", (*owner_proc)->process_id.hostpid);
        return ret;
    }

    ret = devmm_get_memory_attributes(*owner_proc, owner_va, owner_attr);
    if (ret != 0) {
        devmm_svm_other_proc_occupy_put_lock(*owner_proc);
        devmm_drv_err("Query attributes failed. (owner_host_pid=%d; va=%llx)\n",
            (*owner_proc)->process_id.hostpid, owner_va);
        return ret;
    }

    return 0;
}

void devmm_ipc_put_owner_proc_attr(struct devmm_svm_process *owner_proc, struct devmm_memory_attributes *owner_attr)
{
    (void)owner_attr;
    devmm_svm_other_proc_occupy_put_lock(owner_proc);
}


static int devmm_ipc_mem_create_para_check(struct devmm_svm_process *svm_proc,
    u64 vptr, size_t len, struct devmm_memory_attributes *attr)
{
    struct ipc_mem_node *node = NULL;
    u32 *bitmap = devmm_get_page_bitmap(svm_proc, vptr);
    u64 page_bitmap_num = devmm_get_pagecount_by_size(vptr, len, (u32)attr->granularity_size);
    u64 i;

    if ((attr->is_svm_device == 0) || devmm_is_host_agent(attr->devid) || (attr->page_size == 0) ||
        ((vptr % attr->page_size) != 0) || attr->is_mem_export || attr->is_mem_import) {
        devmm_drv_err("Invalid para. (va=0x%llx; page_size=%u; devid=%d; export=%u; import=%u)\n",
            vptr, attr->page_size, attr->devid, attr->is_mem_export, attr->is_mem_import);
        return -EINVAL;
    }

    if (bitmap == NULL) {
        devmm_drv_err("Get bitmap error. (vptr=0x%llx)\n", vptr);
        return -EINVAL;
    }

    if (devmm_check_status_va_info(svm_proc, vptr, len) != 0) {
        devmm_drv_err("Destination vaddress check error. (vptr=0x%llx)\n", vptr);
        return -EINVAL;
    }

    if (devmm_page_bitmap_is_ipc_create_mem(bitmap)) {
        node = devmm_search_ipc_node(svm_proc, vptr, len);
        if (node != NULL) {
            devmm_drv_err("Already created.(va=0x%llx;len=%lu)\n", vptr, len);
            return -EINVAL;
        }
    }

    for (i = 0; i < page_bitmap_num; i++) {
        if (!devmm_page_bitmap_is_page_available(bitmap + i)) {
            devmm_drv_err("Virtual address is invalid. "
                          "(va=0x%llx; page_id=%lld; bitmap=0x%x)\n", vptr, i, *(bitmap + i));
            return -EINVAL;
        }

        if (!devmm_page_bitmap_is_locked_device(bitmap + i)) {
            devmm_drv_err("Virtual address is not device locked. "
                          "(va=0x%llx; page_id=%lld; bitmap=0x%x)\n", vptr, i, *(bitmap + i));
            return -EINVAL;
        }

        if (!devmm_page_bitmap_is_dev_mapped(bitmap + i)) {
            devmm_drv_err("Virtual address is not device maped. "
                          "(va=0x%llx; pageid=%lld; bitmap=0x%x)\n", vptr, i, *(bitmap + i));
            return -EINVAL;
        }
        if (devmm_page_bitmap_is_advise_readonly(bitmap + i)) {
            devmm_drv_err("Readonly mem, not allowed ipc create. "
                          "(va=0x%llx; pageid=%lld; bitmap=0x%x)\n", vptr, i, *(bitmap + i));
            return -EINVAL;
        }
        if (devmm_page_bitmap_is_ipc_open_mem(bitmap + i)) {
            devmm_drv_err("Virtual address is ipc open memory. "
                          "(va=0x%llx; page_id=%lld; bitmap=0x%x)\n", vptr, i, *(bitmap + i));
            return -EINVAL;
        }
    }

    return 0;
}

static int devmm_ipc_mem_open_para_check(struct devmm_svm_process *svm_proc, struct ipc_mem_node *node, u64 vptr)
{
    struct devmm_memory_attributes attr;
    int dst_index, ret;

    ret = devmm_get_memory_attributes(svm_proc, vptr, &attr);
    if (ret != 0) {
        devmm_drv_err("Get_memory_attributes failed. (vptr=0x%llx)\n", vptr);
        return ret;
    }

    if (attr.is_ipc_open) {
        devmm_drv_err("Vptr is ipc memory. (vptr=0x%llx; devid=%d)\n", vptr, attr.devid);
        return -EADDRINUSE;
    }

    if (devmm_is_host_agent(attr.devid)) {
        devmm_drv_err("Vptr is host agent, not surport ipc. (vptr=0x%llx; devid=%d)\n", vptr, attr.devid);
        return -EFAULT;
    }

    ret = devmm_check_status_va_info(svm_proc, vptr, node->len);
    if (ret != 0) {
        devmm_drv_err("Destination address vaddress check error. (vptr=0x%llx; len=%llu)\n", vptr, node->len);
        return ret;
    }

    ret = devmm_ipc_mem_check_pid(node);
    if (ret != 0) {
        devmm_drv_err("Node check pid error. (vptr=0x%llx; ret=%d)\n", vptr, ret);
        return ret;
    }

    if (!devmm_is_ipc_mem_node_valid(node)) {
        devmm_drv_err("Node vaddress is already free. (va=0x%llx; vptr=0x%llx)\n", node->vptr[SVM_IPC_OWNER_ID], vptr);
        return -ENXIO;
    }

    dst_index = devmm_ipc_mem_get_open_index(node, svm_proc->process_id.hostpid, 0);
    if (dst_index >= 0) {
        devmm_drv_err("This node already opened by current thread. (node=\"%s\"; sequence=%u; va=0x%llx)\n",
            node->name, node->sequence, node->vptr[dst_index]);
        return -EADDRINUSE;
    }

    return 0;
}

static int devmm_ipc_vmmas_occupy_inc(struct devmm_svm_process *svm_proc,
    bool is_reserve_addr, u64 va, u64 size)
{
    struct devmm_svm_heap *heap = NULL;

    if (is_reserve_addr == false) {
        return 0;
    }

    heap = devmm_svm_get_heap(svm_proc, va);
    if (heap == NULL) {
        devmm_drv_err("Heap is NULL. (addr=0x%llx)\n", va);
        return -EADDRNOTAVAIL;
    }

    return devmm_vmmas_occupy_inc(&heap->vmma_mng, va, size);
}

static void devmm_ipc_vmmas_occupy_dec(struct devmm_svm_process *svm_proc,
    bool is_reserve_addr, u64 va, u64 size)
{
    struct devmm_svm_heap *heap = NULL;

    if (is_reserve_addr == false) {
        return;
    }

    heap = devmm_svm_get_heap(svm_proc, va);
    if (heap == NULL) {
        devmm_drv_err("Heap is NULL. (addr=0x%llx)\n", va);
        return;
    }

    devmm_vmmas_occupy_dec(&heap->vmma_mng, va, size);
}

static int devmm_ipc_mem_create(struct devmm_svm_process *svm_proc, char *name, u64 vptr, size_t len)
{
    struct devmm_memory_attributes attr;
    struct ipc_mem_node *node = NULL;
    int ret;

    ret = devmm_get_memory_attributes(svm_proc, vptr, &attr);
    if (ret != 0) {
        devmm_drv_err("Get_memory_attributes failed. (vptr=0x%llx)\n", vptr);
        return ret;
    }

    ret = devmm_ipc_mem_create_para_check(svm_proc, vptr, len, &attr);
    if (ret != 0) {
        devmm_drv_err("Create_para_check fail. (va=0x%llx)\n", vptr);
        return ret;
    }

    ret = devmm_ipc_vmmas_occupy_inc(svm_proc, attr.is_reserve_addr, vptr, len);
    if (ret != 0) {
        devmm_drv_err("Ipc ceate vmmas occupy inc failed. (va=0x%llx; size=%lu)\n", vptr, len);
        return ret;
    }

    ret = devmm_creat_ipc_name(name, DEVMM_MAX_NAME_SIZE, attr.devid, attr.vfid);
    if (ret != 0) {
        devmm_drv_err("Get node name. (vptr=0x%llx; ret=%d)\n", vptr, ret);
        devmm_ipc_vmmas_occupy_dec(svm_proc, attr.is_reserve_addr, vptr, len);
        return ret;
    }

    ret = devmm_ipc_node_create(name, attr.devid, attr.vfid, &node);
    if (ret != 0) {
        devmm_drv_err("Create node err. (vptr=0x%llx; ret=%d)\n", vptr, ret);
        devmm_ipc_vmmas_occupy_dec(svm_proc, attr.is_reserve_addr, vptr, len);
        return ret;
    }

    devmm_ipc_mem_node_init(node, svm_proc, vptr, len, &attr);

    ret = devmm_ipc_mem_add_node_to_proc(svm_proc, node, SVM_IPC_OWNER_ID);
    if (ret != 0) {
        devmm_drv_err("Set_ipc_mem_node fail. (ret=%d; va=0x%llx; sequnce=%u)\n",
            ret, vptr, node->sequence);
        devmm_ipc_put_node(node);
        devmm_ipc_vmmas_occupy_dec(svm_proc, attr.is_reserve_addr, vptr, len);
        return ret;
    }

    devmm_ipc_mem_add_proc_to_node(svm_proc, node, SVM_IPC_OWNER_ID);

    devmm_ipc_mem_set_ipc_create_flag(svm_proc, vptr);
    devmm_ipc_mem_set_fst_page_ipc_create_flag(svm_proc, vptr); /* for free va fast check */
    devmm_set_ipc_mem_node_valid_status(node, 1);

    devmm_drv_debug("Create sucess. "
        "(node=\"%s\"; idx=%d; pid=%d; vptr=0x%llx; len=%llx; huge=%d; devid=%u; logic_id=%u; fid=%u)\n",
        node->name, node->sequence, node->pid[SVM_IPC_OWNER_ID], node->vptr[SVM_IPC_OWNER_ID], (u64)len,
        node->is_huge, node->devid, node->logical_devid, node->vfid);

    return 0;
}

static int devmm_ipc_mem_destroy(struct devmm_svm_process *svm_proc, const char *name)
{
    struct devmm_proc_ipc_node *ipc_node = NULL;
    struct ipc_mem_node *node = NULL;
    u64 va, len;
    int ret;

    ret = devmm_ipc_get_node(name, &node);
    if (ret != 0) {
        devmm_drv_err("Find node error. (name=\"%s\")\n", name);
        return -EINVAL;
    }

    devmm_drv_debug("Destroy info. (node=\"%s\"; idx=%d; pid=%d; vptr=0x%llx; is_huge=%d; devid=%u)\n", node->name,
        node->sequence, node->pid[SVM_IPC_OWNER_ID], node->vptr[SVM_IPC_OWNER_ID], node->is_huge, node->devid);

    mutex_lock(&node->node_mutex);

    ipc_node = devmm_search_proc_ipc_node(svm_proc, node->vptr[SVM_IPC_OWNER_ID], node->len);
    if (ipc_node == NULL) {
        devmm_drv_err("Can not find process ipc node. (name=\"%s\"; va=0x%llx; len=0x%llx)\n",
            name, node->vptr[SVM_IPC_OWNER_ID], node->len);
        ret = -EINVAL;
        goto put_node;
    }

    if (!devmm_is_ipc_mem_node_create_by_proc(svm_proc, node)) {
        devmm_drv_err("Node is not created by process. (name=\"%s\"; node=%u)\n", name, node->sequence);
        ret = -EINVAL;
        goto put_node;
    }

    va = node->vptr[SVM_IPC_OWNER_ID];
    len = node->len;

    devmm_set_ipc_mem_node_valid_status(node, 0);

    devmm_ipc_mem_free_open_proc_pages(node);
    devmm_ipc_mem_remove_node_from_proc(svm_proc, ipc_node);
    devmm_ipc_mem_remove_proc_from_node(node, SVM_IPC_OWNER_ID);
    atomic_dec(&node->ref);

    devmm_ipc_vmmas_occupy_dec(svm_proc, node->is_reserve_addr, va, len);

put_node:
    mutex_unlock(&node->node_mutex);
    devmm_ipc_put_node(node);

    return ret;
}

static int devmm_ipc_mem_open(struct devmm_svm_process *svm_proc, const char *name, u64 vptr)
{
    struct ipc_mem_node *node = NULL;
    int dst_index, ret;
    u64 page_num;
    u32 page_size;

    ret = devmm_ipc_get_node(name, &node);
    if (ret != 0) {
        devmm_drv_err("Find node error. (vptr=0x%llx)\n", vptr);
        return -EINVAL;
    }

    mutex_lock(&node->node_mutex);

    ret = devmm_ipc_mem_open_para_check(svm_proc, node, vptr);
    if (ret != 0) {
        goto out;
    }

    dst_index = devmm_ipc_mem_get_idle_index(node, svm_proc->process_id.hostpid, vptr);
    if (dst_index < 0) {
        devmm_drv_err("Node open to much. (vptr=0x%llx;dst_index=%d)\n", vptr, dst_index);
        ret = -EINVAL;
        goto out;
    }

    ret = devmm_ipc_mem_add_node_to_proc(svm_proc, node, (u8)dst_index);
    if (ret != 0) {
        devmm_drv_err("Set_share_mem_node fail. (ret=%d; va=0x%llx)\n", ret, vptr);
        devmm_ipc_mem_node_clear_pid(node, (u32)dst_index);
        goto out;
    }

    devmm_ipc_mem_add_proc_to_node(svm_proc, node, (u32)dst_index);

    page_size = (node->is_huge != 0) ? devmm_svm->device_hpage_size : devmm_svm->svm_page_size;
    page_num = devmm_get_pagecount_by_size(vptr, node->len, page_size);

    devmm_ipc_mem_set_ipc_open_lock_flag(svm_proc, vptr, page_num);

    atomic_inc(&node->ref);

    devmm_drv_debug("Open success. (node=\"%s\"; sequence=%u; va=0x%llx)\n", node->name, node->sequence, vptr);

out:
    mutex_unlock(&node->node_mutex);
    devmm_ipc_put_node(node);

    return ret;
}

static int devmm_ipc_mem_close(struct devmm_svm_process *svm_proc, u64 vptr)
{
    struct devmm_proc_ipc_node *ipc_node = devmm_search_proc_ipc_node(svm_proc, vptr, 0);
    struct ipc_mem_node *node = NULL;
    u32 page_size;

    if (ipc_node == NULL) {
        devmm_drv_err("Address is not ipc memory. (va=0x%llx)\n", vptr);
        return -EINVAL;
    }

    if (ipc_node->idx == SVM_IPC_OWNER_ID) {
        devmm_drv_err("Address is ipc create memory. (va=0x%llx)\n", vptr);
        return -EINVAL;
    }

    node = (struct ipc_mem_node *)ipc_node->ipc_mem_node;
    atomic_inc(&node->ref);

    mutex_lock(&node->node_mutex);

    page_size = (node->is_huge != 0) ? devmm_svm->device_hpage_size : devmm_svm->svm_page_size;
    devmm_ipc_mem_clear_ipc_open_lock_flag(svm_proc, vptr, node->len / page_size);

    devmm_ipc_mem_remove_proc_from_node(node, ipc_node->idx);
    devmm_ipc_mem_remove_node_from_proc(svm_proc, ipc_node);

    atomic_dec(&node->ref);

    devmm_drv_debug("Close success. (node=\"%s\"; sequence=%u; va=0x%llx)\n", node->name, node->sequence, vptr);

    mutex_unlock(&node->node_mutex);
    devmm_ipc_put_node(node);

    return 0;
}

int devmm_ioctl_ipc_mem_create(struct devmm_svm_process *svm_process, struct devmm_ioctl_arg *arg)
{
    struct devmm_mem_ipc_create_para *ipc_create_para = &arg->data.ipc_create_para;
    int ret;

    if (ipc_create_para->name_len < DEVMM_MAX_NAME_SIZE) {
        devmm_drv_err("Input name len error. (Name_len=%u)\n", ipc_create_para->name_len);
        return -EINVAL;
    }

    mutex_lock(&svm_process->ipc_node.node_mutex);
    ret = devmm_ipc_mem_create(svm_process, ipc_create_para->name, ipc_create_para->vptr, ipc_create_para->len);
    mutex_unlock(&svm_process->ipc_node.node_mutex);

    return ret;
}

int devmm_ioctl_ipc_mem_destroy(struct devmm_svm_process *svm_proc, struct devmm_ioctl_arg *arg)
{
    struct devmm_mem_ipc_destroy_para *ipc_destroy_para = &arg->data.ipc_destroy_para;
    int ret;

    ipc_destroy_para->name[DEVMM_MAX_NAME_SIZE - 1] = '\0';

    mutex_lock(&svm_proc->ipc_node.node_mutex);
    ret = devmm_ipc_mem_destroy(svm_proc, (const char *)ipc_destroy_para->name);
    mutex_unlock(&svm_proc->ipc_node.node_mutex);

    return ret;
}

int devmm_ioctl_ipc_mem_open(struct devmm_svm_process *svm_process, struct devmm_ioctl_arg *arg)
{
    struct devmm_mem_ipc_open_para *ipc_open_para = &arg->data.ipc_open_para;
    int ret;

    ipc_open_para->name[DEVMM_MAX_NAME_SIZE - 1] = '\0';

    mutex_lock(&svm_process->ipc_node.node_mutex);
    ret = devmm_ipc_mem_open(svm_process, (const char *)ipc_open_para->name, ipc_open_para->vptr);
    mutex_unlock(&svm_process->ipc_node.node_mutex);

    return ret;
}


int devmm_ioctl_ipc_mem_close(struct devmm_svm_process *svm_pro, struct devmm_ioctl_arg *arg)
{
    struct devmm_mem_ipc_close_para *ipc_close_para = &arg->data.ipc_close_para;
    int ret;

    mutex_lock(&svm_pro->ipc_node.node_mutex);
    ret = devmm_ipc_mem_close(svm_pro, ipc_close_para->vptr);
    mutex_unlock(&svm_pro->ipc_node.node_mutex);

    return ret;
}

int devmm_ioctl_ipc_mem_set_pid(struct devmm_svm_process *svm_pro, struct devmm_ioctl_arg *arg)
{
    struct devmm_mem_ipc_set_pid_para *ipc_set_pid_para = &arg->data.ipc_set_pid_para;
    u64 current_time = (u64)ktime_to_ns(ktime_get());
    struct ipc_mem_node *node = NULL;
    u32 i, j;
    int ret;

    /* prevent string is not end of \0 */
    ipc_set_pid_para->name[DEVMM_MAX_NAME_SIZE - 1] = '\0';
    ret = devmm_ipc_get_node(ipc_set_pid_para->name, &node);
    if (ret != 0) {
        devmm_drv_err("Find node error. (ret=%d)\n", ret);
        return -EINVAL;
    }

    if (node->pid[SVM_IPC_OWNER_ID] != devmm_get_current_pid()) {
        devmm_ipc_put_node(node);
        devmm_drv_err("Pid not create thread.\n");
        return -EINVAL;
    }

    mutex_lock(&node->node_mutex);
    /* find seted num */
    for (i = 0; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
        if (node->white_list[i].pid == DEVMM_SVM_INVALID_PID) {
            break;
        }
    }
    if (((i + ipc_set_pid_para->num) > DEVMM_SECONDARY_PROCESS_NUM) ||
        (ipc_set_pid_para->num > (DEVMM_SECONDARY_PROCESS_NUM - 1))) {
        mutex_unlock(&node->node_mutex);
        devmm_ipc_put_node(node);
        devmm_drv_err("Pid num too large. (seted_num=%u; set_num=%u)\n", i, ipc_set_pid_para->num);
        return -EINVAL;
    }
    for (j = 0; j < ipc_set_pid_para->num; j++) {
        for (i = 0; i < DEVMM_SECONDARY_PROCESS_NUM; i++) {
            if (node->white_list[i].pid == ipc_set_pid_para->set_pid[j]) {
                /* should update the set_time */
                node->white_list[i].set_time = current_time;
                break;
            }
            if (node->white_list[i].pid == DEVMM_SVM_INVALID_PID) {
                node->white_list[i].pid = ipc_set_pid_para->set_pid[j];
                node->white_list[i].set_time = current_time;
                break;
            }
        }
    }
    mutex_unlock(&node->node_mutex);
    devmm_ipc_put_node(node);
    devmm_drv_debug("Set pid success. (num=%d; pid=%d)\n", ipc_set_pid_para->num, ipc_set_pid_para->set_pid[0]);

    return ret;
}

int devmm_ioctl_ipc_mem_query(struct devmm_svm_process *svm_pro, struct devmm_ioctl_arg *arg)
{
    struct devmm_mem_query_size_para *query_size_para = &arg->data.query_size_para;
    struct ipc_mem_node *node = NULL;
    u32 sequence, devid;
    int ret;

    /* prevent string is not end of \0 */
    query_size_para->name[DEVMM_MAX_NAME_SIZE - 1] = '\0';

    ret = devmm_ipc_get_node(query_size_para->name, &node);
    if (ret != 0) {
        devmm_drv_err("Find node error. (node=\"%s\")\n", query_size_para->name);
        return -EINVAL;
    }

    query_size_para->len = node->len;
    query_size_para->is_huge = node->is_huge;
    devid = node->devid;
    sequence = node->sequence;
    devmm_ipc_put_node(node);

    devmm_drv_debug("Query success. (sequence=%u; len=%lu; is_huge=%d; did=%d)\n",
        sequence, query_size_para->len, query_size_para->is_huge, devid);

    return 0;
}
